package cn.javadoc.db.impl;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.util.ClassUtils;

import cn.javadoc.db.DAOImpl;

@SuppressWarnings("rawtypes")
public abstract class AbstractImpl {

	private static final Logger LOG = Logger.getLogger(DAOImpl.class);

	public abstract Object exec(Connection conn, Object[] args)
			throws Exception;

	public Class<?> dao;

	public String sql, methodName;
	public boolean isDynamic;
	private String merge = "merge", update = "update", save = "save",
			fieldMatchReg = "[-_]";
	public Class returnType;
	public LinkedList<Method> paramMethod;
	public List<String> paramName;

	public Class getType(Method method) throws Exception {
		String genType = method.toGenericString();
		// int idx = genType.indexOf('(');
		// genType = genType.substring(0, idx);
		Class type;
		int ixr = genType.indexOf('<');
		if (ixr > 0) {
			genType = genType.substring(ixr + 1, genType.indexOf('>'));
			type = ClassUtils.forName(genType,
					ClassUtils.getDefaultClassLoader());
		} else {
			type = HashMap.class;
		}
		return type;
	}

	public String getSql(Object[] args) {
		if (isDynamic) {
			StringBuffer sb = new StringBuffer();
			for (DySql dy : dySqlList) {
				if (dy.isparam) {
					Object v = args[dy.pos];
					if (v != null) {
						if (!v.getClass().isArray()) {
							if (v.getClass().getName().startsWith("java.lang.")) {
								sb.append(v);
							} else if (Map.class.isAssignableFrom(v.getClass())) {
								sb.append(((Map) v).get(dy.str));
							}
						}
					} else {
						sb.append(v);
					}
				} else {
					sb.append(dy.str);
				}
			}
			return sb.toString();
		} else {
			return sql;
		}
	}

	public PreparedStatement getSqlst(Connection conn, Object[] args)
			throws Exception {
		PreparedStatement stmt = conn.prepareStatement(getSql(args));
		if (args == null || args.length == 0) {
			return stmt;
		}
		if (args.length > 1) {
			for (int i = 0; i < args.length; i++) {
				stmt.setObject(i + 1, args[i]);
			}
			return stmt;
		}
		Class<?> cls = args[0].getClass();
		String pname = cls.getClass().getName();
		if (pname.indexOf('.') < 0
				|| (!cls.isArray() && pname.startsWith("java.lang."))) {
			stmt.setObject(1, args[0]);
			return stmt;
		}

		if (List.class.isAssignableFrom(cls)) {
			List<?> list = (List<?>) args[0];
			for (int i = 0; i < list.size(); i++) {
				stmt.setObject(i + 1, list.get(i));
			}
			return stmt;
		}
		if (cls.isArray()) {
			Object vals[] = (Object[]) args[0];
			for (int i = 0; i < vals.length; i++) {
				stmt.setObject(i + 1, vals[i]);
			}
			return stmt;
		}
		if (paramName != null && paramName.size() > 0) {
			if (Map.class.isAssignableFrom(cls)) {
				@SuppressWarnings("unchecked")
				Map<String, ?> mapv = (Map<String, ?>) args[0];
				for (int i = 0; i < paramName.size(); i++) {
					stmt.setObject(i + 1, mapv.get(paramName.get(i)));
				}
				return stmt;
			}
		}
		if (paramMethod != null && paramMethod.size() > 0) {
			int leng = paramMethod.size();
			for (int i = 0; i < leng; i++) {
				stmt.setObject(i + 1, paramMethod.get(i).invoke(args[0]));
			}
			return stmt;
		}
		return stmt;
	}

	protected void close(Statement stmt, ResultSet rs) {
		try {
			if (rs != null)
				rs.close();
			if (stmt != null)
				stmt.close();
		} catch (SQLException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	protected Object getEle(int w, ResultSetMetaData md, ResultSet rs)
			throws Exception {
		if (returnType == null || Map.class.isAssignableFrom(returnType)) {
			Map<String, Object> map;
			if (returnType != null && returnType.isInterface()) {
				map = new HashMap<String, Object>();
			} else {
				map = (Map<String, Object>) returnType.newInstance();
			}
			for (int i = 1; i <= w; i++) {
				String lab = md.getColumnLabel(i);
				map.put(lab, rs.getObject(i));
			}
			return map;
		}
		String name = returnType.getName();
		if (w == 1
				&& (name.indexOf('.') < 0 || (!returnType.isArray() && name
						.startsWith("java.lang.")))) {
			return getRsObject(rs, 1, returnType);
		}
		if (returnType.isArray()) {
			String n = returnType.getSimpleName();
			if (n.equals("String[]")) {
				String[] row = new String[w];
				for (int i = 1; i <= w; i++) {
					row[i - 1] = rs.getString(i);
				}
				return row;
			}
			if (n.equals("int[]")) {
				int[] row = new int[w];
				for (int i = 1; i <= w; i++) {
					row[i - 1] = rs.getInt(i);
				}
				return row;
			}
			if (n.equals("long[]")) {
				long[] row = new long[w];
				for (int i = 1; i <= w; i++) {
					row[i - 1] = rs.getLong(i);
				}
				return row;
			}
			throw new Exception("unsurport type:" + returnType.getSimpleName());
		}
		if (List.class.isAssignableFrom(returnType)) {
			LinkedList<Object> row = new LinkedList<Object>();
			for (int i = 1; i <= w; i++) {
				row.addLast(rs.getObject(i));
			}
			return row;
		}

		Object ele = returnType.newInstance();
		LinkedList<ResultBean> ms = getResultMethod(md);
		for (ResultBean rb : ms) {
			Object v = getRsObject(rs, rb.index,
					rb.method.getParameterTypes()[0]);
			rb.method.invoke(ele, v);
		}
		return ele;
	}

	private LinkedList<ResultBean> resultMethod;

	private LinkedList<ResultBean> getResultMethod(ResultSetMetaData md)
			throws Exception {
		if (resultMethod != null) {
			return resultMethod;
		}
		resultMethod = new LinkedList<ResultBean>();
		int w = md.getColumnCount();
		Method[] ms = returnType.getMethods();
		StringBuffer notfind = new StringBuffer();
		for (int i = 1; i <= w; i++) {
			String lab = md.getColumnLabel(i);
			if (fieldMatchReg != null) {
				lab = lab.replaceAll(fieldMatchReg, "");
			}
			boolean find = false;
			for (Method m : ms) {
				if (m.getParameterTypes().length == 1) {
					if (m.getName().equalsIgnoreCase("set" + lab)) {
						ResultBean rb = new ResultBean();
						rb.index = i;
						rb.method = m;
						resultMethod.add(rb);
						find = true;
						break;
					}
				}
			}
			if (!find) {
				notfind.append(" ").append(lab);
			}
		}
		if (notfind.length() > 0) {
			LOG.warn("[" + dao.getClass().getName() + ":" + methodName + ":"
					+ returnType.getClass().getName() + ":"
					+ resultMethod.size() + "/" + w + ":" + sql + "]("
					+ notfind.toString() + ")" + (w - resultMethod.size())
					+ " not set values.");
		}
		return resultMethod;
	}

	protected Object getRsObject(ResultSet rs, int i, Class<?> cls)
			throws SQLException {
		if (cls == String.class) {
			return rs.getString(i);
		}
		if (cls == Integer.class || cls == int.class) {
			return rs.getInt(i);
		}
		if (cls == Long.class || cls == long.class) {
			return rs.getLong(i);
		}
		if (cls == short.class || cls == Short.class) {
			return rs.getShort(i);
		}
		if (cls == Date.class) {
			return rs.getTimestamp(i);
		}
		if (cls == Float.class || cls == float.class) {
			return rs.getFloat(i);
		}
		if (cls == char.class || cls == Character.class) {
			return rs.getString(i).charAt(0);
		}
		if (cls == boolean.class || cls == Boolean.class) {
			String val = rs.getString(i);
			return val.equalsIgnoreCase("true") || val.equals("1") ? true
					: false;
		}
		return rs.getObject(i);
	}

	public void setSql(String sqlstr) {
		sql = sqlstr;
		isDynamic = sql.indexOf("$") > 0;
		if (isDynamic) {

		}
	}

	private LinkedList<DySql> dySqlList = new LinkedList<DySql>();

}
